Fork me on GitHub

Spring IOC

注意:所有文章除特别说明外,转载请注明出处.

第2章 Spring Framework的核心 - IoC容器的实现

[TOC]

2.1 Spring IOC容器概述

2.1.1 IOC容器依赖反转模式

通过将对象的依赖注入交给框架或IOC容器来完成。它可以在对象生成或初始化时直接将数据注入到对象中,或可以通过将对象引用注入到对象数据域中的方式来注入对方法调用的依赖。IOC容器进行对象关系的管理,并由IOC容器完成对象的注入。

2.1.2 Spring IOC的应用场景

在Spring中,Spring IoC提供了基本的JavaBean容器,通过IoC模式管理以来关系,并通过依赖注入和AOP切面增强了为JavaBean这样的POJO对象赋予事务管理、生命周期管理等基本功能。

2.2 IoC容器系列的设计与实现 BeanFactory | ApplicationContext

在SpringIoC容器的设计中,主要有两个容器系列:1.实现BeanFactory接口的简单容器系列,实现容器的基本功能。2.ApplicationContext应用上下文,在BeanFactory的基础上增添了功能,它作为容器的高级形态存在。扩展了BeanFactory。

BeanFactory与ApplicationContext都是容器的具体表现形式。BeanFactory体现了Spring为提供给用户使用的IOC容器所设定的最基本的功能规范。在Spring提供IOC容器基本定义基础上,Spring通过定义BeanDefinition来管理基于Spring的应用中各种对象以及他们之间的相互依赖关系。

BeanDefinition抽象了我们对Bean的定义,是让容器起作用的主要数据类型。对Ioc容器而言,BeanDefinition就是对反转依赖模式管理的对象的依赖关系的数据抽象。如:容器里面装的水。

AbstractApplicationContext 是整个容器的核心处理类,是真正的Spring容器的执行者,在内部使用了模板方法。

2.2.2 Spring Ioc容器设计

从BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,这是一条主要的Beanfactory设计路径,该路径定义了基本的IoC容器的规范。而第二条接口设计主线是以ApplicationContext为主的接口设计。

1. BeanFactory应用场景

BeanFactory提供最基本的IOc容器功能。其只是一个接口类,没有给出容器的具体实现。而其它的各种类(DefaultListableBeanFactory/XmlBeanFactory/ApplicationContext)等都可以看做是容器附加某种功能的具体实现。

提示:FactoryBean 与 BeanFactory

通过转移字符&来得到FactoryBean本身,用来区分通过容器来获取FactoryBean产生的对象和获FactoryBean本身。

FactoryBean 和 BeanFactory ,前面是factory,是ioc容器或对象工厂。后者是bean,在spring中,所有的bean都是由BeanFactory(IOC容器)进行管理的。

注意:FactoryBean不是简单的Bean,是一个能够产生或者修饰对象生成的工厂Bean,它的实现和设计模式中的工厂模式类似。

2. BeanFactory容器的设计原理

DefaultListableBeanFactory包含了基本IoC容器所具有的的重要功能,作为Spring中的默认的功能完整的IoC容器来使用。

XmlBeanFactory继承DefaultListableBeanFactory,除了具备基本的功能之外,还具备读取以XML文件定义的BeanDefinition的能力。

XML文件是由XmlBeanFactory初始化的XmlBeanDefinitionReader来处理以XML方式定义的BeanDefinition。构造XmlBeanFactory这个IoC容器需要Resource类给出BeanDefinition的信息来源。Resource是Spring用来封装I/O操作的类,例如ClassPathResource(“*.xml”)等。

XmlBeanFactory 是一个与XML相关的BeanFacotory,也就是说它是一个读取以XML文件方式定义的BeanDefinition的IOC容器。

在使用IOC容器的几个步骤:

1. 创建IOC配置文件的抽象资源,抽象资源包括BeanDefinition的定义信息。

2. 创建一个BeanFactory,这里使用DefaultListableBeanFactory。

3. 创建一个载入BeanDefinition的读取器,如XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory。

4. 从定义好的资源位置读入配置信息。在完成载入和注册Bean之后,需要的ioc容器建立起来,此时可以直接使用ioc容器。
3. ApplicationContext应用场景

ApplicationContext在已有IOC容器基本功能基础上提供附加功能,使得其更方便使用。

1. 支持不同信息源,ApplicationContext扩展了MessageSource接口。支持国际化实现。

2. 访问资源,体现在ResourceLoader和Resource支持上。可从不同地方获取Bean定义的资源。

3. 支持应用事件。继承了接口ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件和Bean 的生命周期的结合为Bean 的管理提供了便利。

4. 在ApplicationContext 中提供的附加服务。这些服务使得基本的 IoC 容器的基本功能更加丰富。

在经过上面的种种讨论我们清楚在应用开发的过程中使用ApplicationContext作为IOC容器的基本形式。

4. ApplicationContext容器设计原理

在ApplicationContext的容器中,常以 FileSystemXmlApplicationContext 来说明 ApplicationContext 容器的设计原理。

ApplicationContext 主要功能在FileSystemXmlApplicationContext 的基类AbstractXmlApplicationContext中已经实现了,在FileSystemXmlApplicationContext 中,作为一个具体的应用上下文,只需要实现和它自身设计相关的2个功能。

第1个功能,如果应用直接使用FileSystemXmlApplicationContext ,对于实例化这个应用上下文的支持,同时启动IoC容器的refresh()过程。

第2个功能,与FileSystemXmlApplicationContext 设计具体相关的功能,这部分与怎样从文件系统中加载XML的bean定义资源有关。

2.3 Ioc容器初始化过程

IOc容器的初始化是由refresh()方法来启动的,该方法标志ioc容器的正式启动。该方法包括BeanDefinition的Resource定位、载入和注册三个基本过程。

提示:Spring 把这三个过程分开,并使用不同的模块来完成,从而方便自己定义IoC 容器的初始化过程。

1. BeanDefinition的Resource定位过程。Resource定位表示BeanDefinition的资源定位过程,它是由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。对于BeanDefinition 的存在形式,可以是文件系统中的通过FileSystemResource 来进行抽象;类路径中定义的Bean 信息可以通过ClassPathResource 来进行抽象。这个定位过程类似于容器寻找数据的过程。

2. BeanDefinition的载入。载入过程是将用户定义好的Bean表示成IOC容器内部的数据结构,该容器内部的数据结构是BeanDefinition。BeanDefinition 实际上就是POJO 对象在IoC 容器中的抽象,通过这个BeanDefinition 定义的数据结构,使IoC 容器能够方便地管理Bean。

3. BeanDefinition的注册。注册过程是通过BeanDefinitionRegistry 接口的实现来完成的。注册过程把载入过程中解析得到BeanDefinition 向IoC 容器进行注册。在IoC 容器内部将BeanDefinition 注入到一个HashMap 中去,IoC 容器就是通过这个HashMap 来持有这些Bean 数据的。

提示:上面的IOC初始化过程不包括Bean依赖注入的实现。在Spring IoC 的设计中,Bean 定义的载入 和 依赖注入是两个独立的过程。依赖注入一般发生在第一个通过getBean() 向容器中获取Bean 的时候,也并不全是这样,例如某个Bean 设置了lazyinit 属性,那么Bean 的依赖注入在其初始化的过程中就已经完成了,而不需要等到整个容器初始化完成之后,第一次使用getBean()才会触发。

2.3.1 BeanDefinition的Resource定位过程

在以编程的方式使用 DefaultListableBeanFactory 时,首先定义一个Resource来定位容器使用的BeanDefinition。使用ClassPathResource,意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition信息 。

ClassPathResource res = new ClassPathResource("beans.xml");

提示:这里定义的Resource并不能由DefaultListableBeanFactory直接使用,Spring通过BeanDefinitionReader来对这些信息进行处理。

我们回到常用的ApplicationContext上,如:FileSystemXmlApplicationContext、ClassPathXmlApplicationContext、XmlWebApplicationContext等。简单地从这些类的名字上分析,可以清楚地看到它们可以提供哪些不同的Resource读入功能。

提示:我们可以清楚的知道,对BeanDefinition 资源定位的过程,是由refresh() 方法触发的,refresh() 方法的调用是在FileSystemXmlApplicationContext 的构造函数中启动的。

在BeanDefinition 定位完成基础上,就可通过返回的Resource 对象进行BeanDefinition 载入。在完成定位过程后,为BeanDefinition 的载入创造了I/O 操作的条件,但具体的数据还没有开始读入,读入将在BeanDefinition的载入和解析中完成。

2.3.2 BeanDefinition的载入和解析

在完成BeanDefinition的Resource定位的后,便可进行BeanDefinition信息的载入过程。该过程相当于把定义的BeanDefinition在IoC容器中转化成一个Spring内部表示的数据结构的过程。IoC容器对Bean的管理和依赖注入功能的实现,是通过对其持有的BeanDefinition进行各种相关操作来完成的。这些BeanDefinition数据在IoC容器中通过一个HashMap来保持和维护。

将定义的BeanDefinition在ioc容器中转换成一个Spring内部表示的数据结构的过程。Ioc容器对Bean管理和依赖注入的实现是通过对其持有的BeanDefinition进行各种相关操作完成的。

从DefaultListableBeanFactory的设计入手来看IoC容器是怎样完成BeanDefinition载入的。先回到IoC容器的初始化入口refresh()方法。该方法最初是在FileSystemXmlApplicationContext的构造函数中被调用的,它的调用标志着容器初始化的开始,这些初始化对象就BeanDefinition数据,初始化入口函数:

启动BeanDefinition的载入

于容器的启动来说,refresh()方法是一个很重要的方法。该方法在AbstractApplicationContext类(是FileSystemXmlApplicationContext的基类)中找到,它详细描述了整个ApplicationContext的初始化过程。如:BeanFactory的更新、MessageSource和PostProcessor的注册。

refresh()方法在ioc容器中执行的过程:

    1. 子类启动refreshBeanFactory()

    2. 设置BeanFactory的后置处理

    ...

读入器的配置,可以在FileSystemXmlApplicationContext 的基类AbstractRefreshableApplicationContext 中的refreshBeanFactory() 方法的实现中查看。refreshBeanFactory() 方法被FileSystemXmlApplicationContext 构造函数中的refresh() 方法所调用。在该方法中,通过createBeanFactory() 创建一个DefaultListableBeanFactory 的IoC 容器供ApplicationContext 使用。同时,还启动了loadBeanDefinitions() 来载入BeanDefinition。

调用的loadBeanDefinitions(Resource res)方法在AbstractBeanDefinitionReader类里是没有实现的,具体的实现在XmlBeanDefinitionReader中。在读取器中,需要得到封装了对XML文件的I/O操作的代表XML文件的Resource,读取器可以在打开I/O流后得到XML的文件对象。有了这个文件对象以后,就可按照Spring的Bean定义规则来对这个XML的文档树进行解析了,该解析是交给BeanDefinitionParserDelegate来完成的。

在完成资源的载入后,需要将载入的BeanDefinition进行解析并转化为IoC容器内部数据结构,这个过程在registerBeanDefinitions()成,具体是由BeanDefinitionsDocumentReader来完成。

BeanDefinition 的载入

首先,通过调用XML的解析器得到document对象。

然后,按照Spring的Bean规则进行解析。按照Spring的Bean规则进行解析的过程是在默认设置好的DefaultBeanDefinitionDocumentReader中实现的。DefaultBeanDefinitionDocumentReader的创建是在后面的方法中完成的,然后再完成BeanDefinition的处理,处理的结果由BeanDefinitionHolder对象持有。这个BeanDefinitionHolder除了持有BeanDefinition对象外,还持有其他与BeanDefinition的使用相关的信息,比如Bean的名字、别名集合等。这个BeanDefinitionHolder的生成是通过对Document文档树的内容进行解析来完成的,可看到该解析过程是由BeanDefinitionParserDelegate实现的(processBeanDefinition方法中实现)的,同时这个解析是与Spring对BeanDefinition的配置规则紧密相关的。

提示:具体的BeanDefinition的解析是在BeanDefinitionParserDelegate中完成的。

在上面过程中介绍了对Bean元素进行解析的过程,也就是BeanDefinition依据XML的定义被创建的过程。这个BeanDefinition可以看成是对定义的抽象。这个数据对象中封装的数据大多都是与定义相关的,也有很多就是我们在定义Bean时看到的那些Spring标记,比如常见的init-method、destroy-method、factory-method等。

beanClass、description、lazyInit这些属性都是在配置bean时经常碰到的,都集中在BeanDefinition。通过解析以后,便可对BeanDefinition元素进行处理,在这个过程中可以看到对Bean定义的相关处理,比如对元素attribute值的处理,对元素属性值的处理,对构造函数设置的处理,等等。

2.3.3 BeanDefinition在ioc容器中的注册

BeanDefinition在IoC容器中载入和解析的过程完成以后,用户定义的BeanDefinition信息已经在IoC容器内建立起了自己的数据结构以及相应的数据表示,但此时这些数据还不能供IoC容器直接使用,需要在IoC容器中对这些BeanDefinition数据进行注册。在DefaultListableBeanFactory中,是通过一个HashMap来持有载入的BeanDefinition的,这个HashMap的定义在DefaultListableBeanFactory中可以看到,如下所示。

private final Map beanDefinitionMap = new  ConcurrentHashMap();

将解析得到的BeanDefinition向IoC容器中的beanDefinitionMap注册的过程是在载入BeanDefinition完成后进行的。

在DefaultListableBeanFactory中实现了BeanDefinitionRegistry的接口,该接口的实现完成BeanDefinition向容器的注册。注册过程就是把解析得到的BeanDefinition设置到 hashMap中去。需要注意的是,如果遇到同名的BeanDefinition,进行处理的时候需要依据allowBeanDefinitionOverriding的配置来完成。

完成了BeanDefinition的注册,就完成了IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,它们都在beanDefinitionMap里被检索和使用。容器的作用就是对这些信息进行处理和维护。

总结:初始化过程主要工作是在ioc容器中建立BeanDefinition数据映射。

2.4 ioc容器的依赖注入

初始化过程主要工作是在ioc容器中建立BeanDefinition数据映射。

当前IoC容器已经载入了用户定义的Bean信息,我们注意到用户第一次向IoC容器索要Bean时,即调用getBean()的接口定义,触发依赖注入。

在BeanDefinition信息中通过控制 lazy-init 属性来让容器完成对Bean的预实例化。它是在初始化过程中完成的。

重点来说,getBean是依赖注入的起点,之后会调用createBean。在这个过程中,Bean对象会依据BeanDefinition定义的要求生成。在AbstractAutowireCapableBeanFactory中实现了这个createBean,createBean不但生成了需要的Bean,还对Bean初始化进行了处理,比如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等。

与依赖注入关系特别密切的方法有createBeanInstance()和populateBean()。在createBeanInstance()中生成了Bean所包含的Java对象,这个对象的生成有很多种不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,这些生成方式都是由相关的BeanDefinition来指定的。

IoC容器中,要了解怎样使用cglib来生成Bean对象,需要看一下SimpleInstantiationStrategy类。这个Strategy是Spring用来生成Bean对象的默认类,它提供了两种实例化Java对象的方法,一种是通过BeanUtils,它使用了JDK的反射功能,一种是通过前面提到的CGLIB来生成,使用SimpleInstantiationStrategy生成Java对象。

在实例化Bean对象生成的基础上,怎样把这些Bean对象的依赖关系设置好,完成整个依赖注入过程。这个过程涉及对各种Bean对象的属性的处理过程(即依赖关系处理的过程),这些依赖关系处理的依据就是已经解析得到的BeanDefinition。这个过程涉及到 populateBean() 方法。

通过使用BeanDefinitionResolver来对BeanDefinition进行解析,然后注入到property中。下面到BeanDefinitionValueResolver中去看一下解析过程的实现。

这两种属性的注入都调用了resolveValueIfNecessary,这个方法包含了所有对注入类型的处理。

在完成这个解析过程后,已经为依赖注入准备好了条件,这是真正把Bean对象设置到它所依赖的另一个Bean的属性中去的地方,其中处理的属性是各种各样的。依赖注入的发生是在BeanWrapper的setPropertyValues中实现的,具体的完成却是在BeanWrapper的子类BeanWrapperImpl中实现的。

在Bean的创建和对象依赖注入的过程中,需要依据BeanDefinition中的信息来递归地完成依赖注入。从上面的几个递归过程中可以看到,这些递归都是以getBean()方法为入口的。一个递归是在上下文体系中查找需要的Bean和创建Bean的递归调用。另一个递归是在依赖注入时,通过递归调用容器的getBean()方法,得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入。在对Bean的属性进行依赖注入时,解析的过程也是一个递归的过程。这样根据依赖关系,一层一层地完成Bean的创建和注入,直到最后完成当前Bean的创建。有了这个顶层Bean的创建和对它的属性依赖注入的完成,意味着和当前Bean相关的整个依赖链的注入也完成了。

在Bean创建和依赖注入完成以后,在IoC容器中建立起一系列依靠依赖关系联系起来的Bean,这个Bean已经不是简单的Java对象了。该Bean系列以及Bean之间的依赖关系建立完成以后,通过IoC容器的相关接口方法,就可以非常方便地供上层应用使用了。

2.5 容器的其它特性的设计与实现

2.5.1 ApplicationContext和Bean的初始化和销毁

  1. 初始化

对于BeanFactory,特别是ApplicationContext,容器自身也有一个初始化和销毁关闭的过程。容器的实现是通过ioc管理Bean的生命周期来实现的。Ioc容器的Bean生命周期。

在调用Bean初始化方法之前会调用一系列aware接口实现,将相关的BeanName、BeanClassLoader以及BeanFactory注入到Bean中。

对 ApplicationContext 启动 的过程是在AbstractApplicationContext中实现的。在使用应用上下文时需要做一些准备工作,这些准备工作在prepareBeanFactory()方法中实现。在这个方法中,为容器配置了ClassLoader、PropertyEditor和BeanPostProcessor等,从而为容器的启动做好了必要的准备工作。

在容器要关闭时,也需要完成一系列的工作,这些工作在doClose( )方法中完成。在这个方法中,先发出容器关闭的信号,然后将Bean逐个关闭,最后关闭容器自身。

在容器的初始化和销毁的设计与实现过程中需要区分Bean的初始化和销毁过程。Spring IoC容器提供了相关的功能,可以让应用定制Bean的初始化和销毁过程。容器的实现是通过IoC管理Bean的生命周期来实现的。Spring IoC容器在对Bean的生命周期进行管理时提供了Bean生命周期各个时间点的回调。在分析Bean初始化和销毁过程的设计之前,简要介绍一下IoC容器中的Bean生命周期。

1. Bean实例的创建。

2. 为Bean实例设置属性

3. 调用Bean的初始化方法

4. 应用可以通过Ioc容器使用Bean

5. 当容器关闭时,调用Bean的销毁方法

Bean的初始化方法调用是在initializeBean()方法实现的。

在调用Bean的初始化方法之前,会调用一系列的aware接口实现,把相关的BeanName、BeanClassLoader,以及BeanFactoy注入到Bean中去。接着会看到对invokeInitMethods的调用,这时还会看到启动afterPropertiesSet的过程,当然,这需要Bean实现InitializingBean的接口,对应的初始化处理可以在InitializingBean接口的afterPropertiesSet方法中实现,这里同样是对Bean的一个回调。

最后,还会看到判断Bean是否配置有initMethod,如果有,那么通过invokeCustomInitMethod方法来直接调用,最终完成Bean的初始化。

在这个对initMethod的调用中,可以看到首先需要得到Bean定义的initMethod,然后通过JDK的反射机制得到Method对象,直接调用在Bean定义中声明的初始化方法。

  1. 销毁

我们可以看到对Bean的销毁过程,首先对postProcessBeforeDestruction进行调用,然后调用Bean的destroy方法,最后是对Bean的自定义销毁方法的调用,整个过程和前面的初始化过程很类似。

2.5.2 lazy-init属性和预实例化

在IOC容器初始化过程中,主语的工作是对BeanDefinition的定位、载入、解析和注册。此时依赖注入并没有发生,依赖注入发生在应用第一次向容器索要 Bean 时。向容器索要Bean是通过getBean()方法的调用完成的。该getBean是容器提供Bean服务的最基本的接口。

2.5.3 FactoryBean的实现

FactoryBean为应用生成需要的对象,这些对象往往是经过特殊处理的,如ProxyFactoryBean这样的特殊Bean。FactoryBean的生产特性是在getBean中起作用的,看下面的调用:

bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

在getObjectForBeanInstance的实现方法中可以看到在FactoryBean中常见的getObject方法的接口。FactoryBean类似于AbstractFactory抽象工厂,getObjectForBeanInstance()方法类似于createProduceA()这样的生产接口,具体的FactoryBean实现如:TransactionProxyBean就是具体的工厂实现。

2.5.4 BeanPostProcessor的实现

BeanPostProcessor是使用IoC容器时经常会遇到的一个特性,这个Bean的后置处理器是一个监听器,它可以监听容器触发的事件。将它向IoC容器注册后,容器中管理的Bean具备了接收IoC容器事件回调的能力。BeanPostProcessor的使用非常简单,只需要通过设计一个具体的后置处理器来实现。同时,这个具体的后置处理器需要实现接口类BeanPostProcessor,然后设置到XML的Bean配置文件中。这个BeanPostProcessor是一个接口类,它有两个接口方法,一个是postProcessBeforeInitialization,在Bean初始化前提供回调入口;一个是postProcessAfterInitialization,在Bean的初始化后提供回调入口,这两个回调的触发都是和容器管理Bean的生命周期相关的。这两个回调方法的参数都是一样的,分别是Bean的实例化对象和Bean的名字。

postProcessBeforeInitialization是在populateBean完成之后被调用的。从BeanPostProcessor中的一个回调接口入手,对另一个回调接口postProcessAfterInitialization方法的调用,实际上也是在同一个地方封装完成的,这个地方就是populateBean方法中的initializeBean调用。在前面对IoC的依赖注入进行分析时,对这个populateBean有过分析,这个方法实际上完成了Bean的依赖注入。在容器中建立Bean的依赖关系,是容器功能实现的一个很重要的部分。

在initializeBean方法中,需要使用Bean的名字,完成依赖注入以后的Bean对象,以及这个Bean对应的BeanDefinition。在这些输入的帮助下,完成Bean的初始化工作,这些工作包括为类型是BeanNameAware的Bean设置Bean的名字,类型是BeanClassLoaderAware的Bean设置类装载器,类型是BeanFactoryAware的Bean设置自身所在的IoC容器以供回调使用,当然,还有对postProcessBeforeInitialization/postProcessAfterInitialization的回调和初始化属性init-method的处理等。经过这一系列的初始化处理之后,得到的结果就是可以正常使用的由IoC容器托管的Bean了。

2.5.5 autowiring(自动依赖装配)的实现

在之前我们一直是通过 BeanDefinition 的属性值和构造函数以显式的方式对Bean的依赖关系进行管理的。在Spring IoC容器中还提供了自动依赖装配的方式。在自动装配中,不需要对Bean属性做显式的依赖关系声明,只需要配置好autowiring属性,IoC容器会根据这个属性的配置,使用反射自动查找属性的类型或者名字,然后基于属性的类型或名字来自动匹配IoC容器中的Bean,从而自动地完成依赖注入。

autowiring属性是在对Bean属性进行依赖注入时起作用。对autowirng属性进行处理,从而完成对Bean属性的自动依赖装配,是在populateBean中实现的。对属性autowiring的处理是populateBean处理过程的一个部分。在populateBean的实现中,在处理一般的Bean之前,先对autowiring属性进行处理。如果当前的Bean配置了autowire_by_name和autowire_by_type属性,那么调用相应的autowireByName方法和autowireByType方法。例如,对于autowire_by_name,它首先通过反射机制从当前Bean中得到需要注入的属性名,然后使用这个属性名向容器申请与之同名的Bean,这样实际又触发了另一个Bean的生成和依赖注入的过程。

在对autowiring类型做了一些简单的逻辑判断以后,通过调用autowireByName和autowireByType来完成自动依赖装配。

对autowireByName来说,它首先需要得到当前Bean的属性名,这些属性名已经在BeanWrapper和BeanDefinition中封装好了,然后是对这一系列属性名进行匹配的过程。在匹配的过程中,因为已经有了属性的名字,所以可以直接使用属性名作为Bean名字向容器索取Bean,这个getBean会触发当前Bean的依赖Bean的依赖注入,从而得到属性对应的依赖Bean。在执行完这个getBean后,把这个依赖Bean注入到当前Bean的属性中去,这样就完成了通过这个依赖属性名自动完成依赖注入的过程。autowireByType的实现和autowireByName的实现过程是非常类似的。

2.5.6 Bean的依赖检查

在一般情况下,Bean的依赖注入是在应用第一次向容器索取Bean的时候发生,在这个时候,不能保证注入一定能够成功,如果需要重新检查这些依赖关系的有效性,会是一件很繁琐的事情。为了解决这样的问题,在Spring IoC容器中,设计了一个依赖检查特性,通过它,Spring可以帮助应用检查是否所有的属性都已经被正确设置。在具体使用的时候,应用只需要在Bean定义中设置dependency-check属性来指定依赖检查模式即可,这里可以将属性设置为none、simple、object、all四种模式,默认的模式是none。

2.5.7 Bean对IoC容器的感知

容器管理的Bean一般不需要了解容器的状态和直接使用容器,但在某些情况下,是需要在Bean中直接对IoC容器进行操作的,这时候,就需要在Bean中设定对容器的感知。Spring IoC容器也提供了该功能,它是通过特定的aware接口来完成的。aware接口有以下这些:

BeanNameAware ,可以在Bean中得到它在IoC容器中的Bean实例名称。

BeanFactoryAware,可以在Bean中得到Bean所在的IoC容器,从而直接在Bean中使用IoC容器的服务。

ApplicationContextAware,可以在Bean中得到Bean所在的应用上下文,从而直接在Bean中使用应用上下文的服务。

MessageSourceAware,在Bean中可以得到消息源。

ApplicationEventPublisherAware,在Bean中可以得到应用上下文的事件发布器,从而可以在Bean中发布应用上下文的事件。

ResourceLoaderAware,在Bean中可以得到ResourceLoader,从而在Bean中使用ResourceLoader加载外部对应的Resource资源。

在设置Bean的属性之后,调用初始化回调方法之前,Spring会调用aware接口中的setter方法。以ApplicationContextAware为例,分析对应的设计和实现。这个接口定义得很简单。

这里只有一个方法setApplicationContext(ApplicationContext applicationContext),它是一个回调函数,在Bean中通过实现这个函数,可以在容器回调该aware接口方法时使注入的applicationContext引用在Bean中保存下来,供Bean需要使用ApplicationContext的基本服务时使用。这个对setApplicationContext方法的回调是由容器自动完成的。可以看到,一个ApplicationContextAwareProcessor作为BeanPostProcessor的实现,对一系列的aware回调进行了调用,比如对ResourceLoaderAware接口的调用,对ApplicationEventPublisherAware接口的调用,以及对MessageSourceAware和ApplicationContextAware的接口调用等。

本文标题:Spring IOC

文章作者:Bangjin-Hu

发布时间:2019年10月15日 - 09:22:26

最后更新:2020年03月30日 - 08:17:46

原始链接:http://bangjinhu.github.io/undefined/第2章 Spring IoC容器的实现/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Bangjin-Hu wechat
欢迎扫码关注微信公众号,订阅我的微信公众号.
坚持原创技术分享,您的支持是我创作的动力.